<?php
/**
 * Memcahce 类
 * 
 *
 * edit the singleton() metod
 * and define the list of memcached servers in a 2-d array
 * in the format
 * array(
 * array('192.168.0.1'=>'11211'),
 * array('192.168.0.2'=>'11211'),
 * );
 * 
 * @category   Framework
 * @package    Cache
 * @author     Xinze <xinze@live.cn>
 * @copyright  Copyright (c) 2010, 黄新泽
 * @version    1.0
 * @todo       
 */
class Zero_Cache_Memcache
{
    /**
     * 对象本身
     * @var int
     */
    public static $_instance;
    public static $servers;

    public static $expire;
    public static $key;
    public static $cacheServer;

    /**
     * 构造函数
     *
     * @param array $servers
     */
    protected function __construct() 
    {
    }

    final public static function getInstance() 
    {
        if (!(self::$_instance instanceof self))
        {
            self::$_instance = new self();

            self::$_instance->init();
        }

        return self::$_instance;
    }

    public function init()
    {
        $config_cache = Zero_Registry::get('config_cache');

        self::$servers = $config_cache['memcache'];

        $this->cacheServer = new Memcache();

        //（注：addServer没有连接到服务器的动作，所以在memcache进程没有启动的时候，执行addServer成功也会返回true）
        foreach (self::$servers as $key=>$host)
        {
            $flag = $this->cacheServer->addServer(key($host), current($host));
        }

        //根据我的测试结果，setCompressThreshold方法会忽略Memcache::set的flag参数。
        //$this->cacheServer[$server_id]->setCompressThreshold(2048, 0.2);
    }

    /**
     * Returns the value stored in the memory by it's key
     *
     * @param string $key
     * 
     * @return mix
     */
    public function get($key, $group=null) 
    {
        self::$key = $key;

        if (is_array($key))
        {
            $key = array_map('md5', $key);
        }
        else
        {
            $key = md5($key);
        }

        return $this->cacheServer->get($key);
    }

    /**
     * Store the value in the memcache memory (overwrite if key exists)
     *
     * @param string $key
     * @param mix $var
     * @param bool $compress
     * @param int $expire (seconds before item expires)
     * 
     * @return bool
     */
    public function save($var, $key=null, $group=null, $compress=0, $expire=0)
    {
        $expire = self::$expire;

        if (null === $key)
        {
            $key = self::$key;
        }

        //如果有组的概念
        if ($group)
        {
            //得到本组下的Key
            $group_key_row = $this->get($group);

            if (!is_array($group_key_row))
            {
                $group_key_row = array();
            }

            if (!in_array($key, $group_key_row))
            {
                $group_key_row[] = $key;
            }

            $this->set(md5($group), $group_key_row);
        }

        $key = md5($key);

        return $this->set($key, $var, $compress, $expire);
    }

    /**
     * Store the value in the memcache memory (overwrite if key exists)
     *
     * @param string $key
     * @param mix $var
     * @param bool $compress
     * @param int $expire (seconds before item expires)
     * 
     * @return bool
     */
    public function set($key, $var, $compress=0, $expire=0)
    {
        return $this->cacheServer->set($key, $var, $compress ? MEMCACHE_COMPRESSED : null, $expire);
    }

    /**
     * Set the value in memcache if the value does not exist; returns FALSE if value exists
     *
     * @param sting $key
     * @param mix $var
     * @param bool $compress
     * @param int $expire
     * 
     * @return bool
     */
    public function add($key, $var, $compress = 0, $expire = 0) 
    {
        $key = md5($key);

        return $this->cacheServer->add($key, $var, $compress ? MEMCACHE_COMPRESSED : null, $expire);
    }

    /**
     * Replace an existing value
     *
     * @param string $key
     * @param mix $var
     * @param bool $compress
     * @param int $expire
     * 
     * @return bool
     */
    public function replace($key, $var, $compress = 0, $expire = 0) 
    {
        $key = md5($key);

        return $this->cacheServer->replace($key, $var, $compress ? MEMCACHE_COMPRESSED : null, $expire);
    }

    /**
     * Delete a record or set a timeout
     *
     * @param string $key
     * @param int $timeout
     * 
     * @return bool
     */
    public function delete($key, $timeout = 0) 
    {
        $key = md5($key);

        return $this->cacheServer->delete($key, $timeout);
    }

    /**
     * Delete a record or set a timeout
     *
     * @param string $key
     * @param int $timeout
     * 
     * @return bool
     */
    public function remove($key, $timeout = 0) 
    {
        $key = md5($key);

        return $this->cacheServer->delete($key, $timeout);
    }


    /**
     * Delete a record or set a timeout
     *
     * @param string $key
     * @param int $timeout
     * 
     * @return bool
     */
    public function deleteGroup($group, $timeout = 0) 
    {
        $group_key_row = $this->get($group);

        if (!empty($group_key_row))
        {
            foreach ($group_key_row as $key)
            {
                $this->delete($key, $timeout);
            }
        }

        $this->delete($group, $timeout);
    }

    public function clean($group, $timeout = 0) 
    {
        $group_key_row = $this->get($group);

        if (!empty($group_key_row))
        {
            foreach ($group_key_row as $key)
            {
                $this->delete($key, $timeout);
            }
        }

        $this->delete($group, $timeout);
    }

    /**
     * Increment an existing integer value
     *
     * @param string $key
     * @param mix $value
     * 
     * @return bool
     */
    public function increment($key, $value = 1) 
    {
        $key = md5($key);

        return $this->cacheServer->increment($key, $value);
    }

    /**
     * Decrement an existing value
     *
     * @param string $key
     * @param mix $value
     * 
     * @return bool
     */
    public function decrement($key, $value = 1) 
    {
        $key = md5($key);

        return $this->cacheServer->decrement($key, $value);
    }

    public function getExtendedStats() 
    {
        return $this->cacheServer->getExtendedStats();
    }

    public function getStats() 
    {
        return $this->cacheServer->getStats();
    }

    public function getVersion() 
    {
        return $this->cacheServer->getVersion();
    }

    /**
     * Clear the cache
        如果要清空memcache的items，常用的办法是什么？杀掉重启？如果有n台memcache需要重启怎么办？挨个做一遍？

        很简单，假设memcached运行在本地的11211端口，那么跑一下命令行：
        $ echo ”flush_all” | nc localhost 11211

        注：flush并不会将items删除，只是将所有的items标记为expired
     *
     * @return void
     */
    public function flush() 
    {
        $this->cacheServer->flush();
    }

    /**
     * close  all the memcache connect
     *
     * @return void
     */
    public function close() 
    {
        $this->cacheServer->close();
    }

    final private function __clone() 
    {
    } 
}
?>